package com.eurobcoin.trading;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.apache.ibatis.session.SqlSession;

import com.eurobcoin.dao.mapper.BuyOrderMapper;
import com.eurobcoin.dao.mapper.CurrencyPairMapper;
import com.eurobcoin.dao.mapper.SellOrderMapper;
import com.eurobcoin.dao.mapper.TPCurrencyMapper;
import com.eurobcoin.dao.mapper.UserMapper;
import com.eurobcoin.dao.mapper.UserOrderAuditMapper;
import com.eurobcoin.dao.model.BuyOrder;
import com.eurobcoin.dao.model.BuyOrderExample;
import com.eurobcoin.dao.model.CurrencyPair;
import com.eurobcoin.dao.model.CurrencyPairExample;
import com.eurobcoin.dao.model.SellOrder;
import com.eurobcoin.dao.model.SellOrderExample;
import com.eurobcoin.dao.model.TPCurrency;
import com.eurobcoin.dao.model.User;
import com.eurobcoin.dao.model.UserOrderAudit;

public class TradingManager {
	
	private List<TradingPair> tradingPairs;

	public List<TradingOrder> getBuyOrders(
			SqlSession sqlSession, 
			int leftCurrencyId, 
			int rightCurrencyId,
			int firstCurrencyDecimals,
			int secondCurrencyDecimals,
			Locale locale) {
		
		List<TradingOrder> ret = new ArrayList<TradingOrder>();
		
		BuyOrderMapper buyOrderMapper = sqlSession.getMapper(BuyOrderMapper.class);
		BuyOrderExample buyOrderExample = new BuyOrderExample();
		buyOrderExample.setOrderByClause("buo_order_price DESC");
		
		BuyOrderExample.Criteria criteria = buyOrderExample.createCriteria();
		criteria.andBuo_left_currency_idEqualTo((short)leftCurrencyId);
		criteria.andBuo_right_currency_idEqualTo((short)rightCurrencyId);
		
		List<BuyOrder> buyOrderList = buyOrderMapper.selectByExample(buyOrderExample);
		
		for(BuyOrder buyOrder : buyOrderList) {
			TradingOrder tradingOrder = new TradingOrder();
			tradingOrder.setAmount(buyOrder.getBuo_order_amount());
			tradingOrder.setPrice(buyOrder.getBuo_order_price());
			tradingOrder.setLeftCurrencyDecimals(firstCurrencyDecimals);
			tradingOrder.setRightCurrencyDecimals(secondCurrencyDecimals);
			tradingOrder.setLocale(locale);
			
			long factor = (long)Math.pow(10.0, (double)firstCurrencyDecimals);
			long totalPrice = (long)((double)buyOrder.getBuo_order_amount() * (double)buyOrder.getBuo_order_price() / (double)factor);
			
			tradingOrder.setTotalPrice(totalPrice);
			
			ret.add(tradingOrder);
		}
		
		return ret;
		
	}
	
	public TradingOperationResult buyOrder(
			SqlSession sqlSession, 
			int userId,
			String username,
			TradingPair tradingPair,
			long buyAmount, 
			long buyPrice) {
	
		TradingOperationResult ret = new TradingOperationResult();
		ret.setUserId(userId);
		ret.setAmount(buyAmount);
		ret.setPrice(buyPrice);
		ret.setFirstCurrencyId(tradingPair.getFirstCurrencyId());
		ret.setSecondCurrencyId(tradingPair.getSecondCurrencyId());
		ret.setOperationType(TradingOperationResult.BUY_OPERATION_TYPE);
		
		// select sell orders
		SellOrderMapper sellOrderMapper = sqlSession.getMapper(SellOrderMapper.class);
		SellOrderExample sellOrderExample = new SellOrderExample();
		sellOrderExample.setOrderByClause("seo_order_price ASC");
		
		SellOrderExample.Criteria criteria = sellOrderExample.createCriteria();
		criteria.andSeo_order_priceLessThanOrEqualTo((int)buyPrice);
		criteria.andSeo_left_currency_idEqualTo((short)tradingPair.getFirstCurrencyId());
		criteria.andSeo_right_currency_idEqualTo((short)tradingPair.getSecondCurrencyId());
		
		List<SellOrder> sellOrderList = sellOrderMapper.selectByExample(sellOrderExample);
		
		long remainingAmount = buyAmount;
		
		for(SellOrder sellOrder : sellOrderList) {
			
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			User user = userMapper.selectByPrimaryKey(sellOrder.getSeo_usr_id());
			
			TradingPartialOperation tradingPartialOperation = new TradingPartialOperation();
			
			if(remainingAmount >= sellOrder.getSeo_order_amount()) {
				
				tradingPartialOperation.setAmount(sellOrder.getSeo_order_amount());
				tradingPartialOperation.setPrice(sellOrder.getSeo_order_price());
				tradingPartialOperation.setUserId(user.getUsr_id());
				
				ret.addTradingPartialOperation(tradingPartialOperation);
				
				// delete order
				sellOrderMapper.deleteByPrimaryKey(sellOrder.getSeo_order_id());
				
			} else if(remainingAmount < sellOrder.getSeo_order_amount()) {
				
				tradingPartialOperation.setAmount(remainingAmount);
				tradingPartialOperation.setPrice(sellOrder.getSeo_order_price());
				tradingPartialOperation.setUserId(user.getUsr_id());
				
				ret.addTradingPartialOperation(tradingPartialOperation);
				
				// update order
				long newAmount = sellOrder.getSeo_order_amount() - remainingAmount;
				sellOrder.setSeo_order_amount((int)newAmount);
				
				sellOrderMapper.updateByPrimaryKey(sellOrder);
			}
			
			remainingAmount = remainingAmount - tradingPartialOperation.getAmount();
		}
		
		if(remainingAmount > 0) {
			BuyOrderMapper buyOrderMapper = sqlSession.getMapper(BuyOrderMapper.class);
			
			// store order
			BuyOrder buyOrder = new BuyOrder();
			buyOrder.setBuo_left_currency_id((short)tradingPair.getFirstCurrencyId());
			buyOrder.setBuo_order_amount((int)remainingAmount);
			buyOrder.setBuo_order_price((int)buyPrice);
			buyOrder.setBuo_right_currency_id((short)tradingPair.getSecondCurrencyId());
			buyOrder.setBuo_usr_id(userId);
			buyOrder.setBuo_timestamp(new Date());
			
			buyOrderMapper.insert(buyOrder);
		}
		
		ret.setCode(TradingOperationResult.OPERATION_OK_CODE);
		
		return ret;
		
	}
	
	public TradingOperationResult sellOrder(SqlSession sqlSession, 
			int userId,
			String username,
			TradingPair tradingPair,
			long sellAmount, 
			long sellPrice) {
		
		TradingOperationResult ret = new TradingOperationResult();
		ret.setUserId(userId);
		ret.setAmount(sellAmount);
		ret.setPrice(sellPrice);
		ret.setFirstCurrencyId(tradingPair.getFirstCurrencyId());
		ret.setSecondCurrencyId(tradingPair.getSecondCurrencyId());
		ret.setOperationType(TradingOperationResult.SELL_OPERATION_TYPE);
		
		// select buy orders
		BuyOrderMapper buyOrderMapper = sqlSession.getMapper(BuyOrderMapper.class);
		BuyOrderExample buyOrderExample = new BuyOrderExample();
		buyOrderExample.setOrderByClause("buo_order_price DESC");
		
		BuyOrderExample.Criteria criteria = buyOrderExample.createCriteria();
		criteria.andBuo_order_priceGreaterThanOrEqualTo((int)sellPrice);
		criteria.andBuo_left_currency_idEqualTo((short)tradingPair.getFirstCurrencyId());
		criteria.andBuo_right_currency_idEqualTo((short)tradingPair.getSecondCurrencyId());
		
		List<BuyOrder> buyOrderList = buyOrderMapper.selectByExample(buyOrderExample);
		
		long remainingAmount = sellAmount;
		
		for(BuyOrder buyOrder : buyOrderList) {
			
			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
			User user = userMapper.selectByPrimaryKey(buyOrder.getBuo_usr_id());
			
			TradingPartialOperation tradingPartialOperation = new TradingPartialOperation();
			
			if(remainingAmount >= buyOrder.getBuo_order_amount()) {
				
				tradingPartialOperation.setAmount(buyOrder.getBuo_order_amount());
				tradingPartialOperation.setPrice(buyOrder.getBuo_order_price());
				tradingPartialOperation.setUserId(user.getUsr_id());
				
				ret.addTradingPartialOperation(tradingPartialOperation);
				
				// delete order
				buyOrderMapper.deleteByPrimaryKey(buyOrder.getBuo_order_id());
				
			} else if(remainingAmount < buyOrder.getBuo_order_amount()) {
				
				tradingPartialOperation.setAmount(remainingAmount);
				tradingPartialOperation.setPrice(buyOrder.getBuo_order_price());
				tradingPartialOperation.setUserId(user.getUsr_id());
				
				ret.addTradingPartialOperation(tradingPartialOperation);
				
				// update order
				long newAmount = buyOrder.getBuo_order_amount() - remainingAmount;
				buyOrder.setBuo_order_amount((int)newAmount);
				
				buyOrderMapper.updateByPrimaryKey(buyOrder);
			}
			
			remainingAmount = remainingAmount - tradingPartialOperation.getAmount();
		}
		
		if(remainingAmount > 0) {
			SellOrderMapper sellOrderMapper = sqlSession.getMapper(SellOrderMapper.class);
			
			// store order
			SellOrder sellOrder = new SellOrder();
			sellOrder.setSeo_left_currency_id((short)tradingPair.getFirstCurrencyId());
			sellOrder.setSeo_order_amount((int)remainingAmount);
			sellOrder.setSeo_order_price((int)sellPrice);
			sellOrder.setSeo_right_currency_id((short)tradingPair.getSecondCurrencyId());
			sellOrder.setSeo_usr_id(userId);
			sellOrder.setSeo_timestamp(new Date());
			
			sellOrderMapper.insert(sellOrder);
		}
		
		ret.setCode(TradingOperationResult.OPERATION_OK_CODE);
		
		return ret;
		
	}
	
	public TradingPair getTradingPairById(SqlSession sqlSession, int pairId) {
		
		TPCurrencyMapper currencyMapper = sqlSession.getMapper(TPCurrencyMapper.class);
		CurrencyPairMapper mapper = sqlSession.getMapper(CurrencyPairMapper.class);
		CurrencyPair currencyPair = mapper.selectByPrimaryKey(pairId);
		
		TradingPair ret = null;
		
		if(currencyPair != null) {
			ret = new TradingPair();
			ret.setPairId(pairId);
			
			ret.setFirstCurrencyId(currencyPair.getTpp_first_currency());
			ret.setSecondCurrencyId(currencyPair.getTpp_second_currency());
			
			TPCurrency cur1 = currencyMapper.selectByPrimaryKey(ret.getFirstCurrencyId());
			TPCurrency cur2 = currencyMapper.selectByPrimaryKey(ret.getSecondCurrencyId());
			
			ret.setFirstCurrencyCode(cur1.getCur_code());
			ret.setSecondCurrencyCode(cur2.getCur_code());
			
			ret.setFirstCurrencyDecimals(cur1.getCur_decimals());
			ret.setSecondCurrencyDecimals(cur2.getCur_decimals());
		}
		
		return ret;
	}
	
	public List<TradingPair> getTradingPairs() {
		return this.tradingPairs;
	}
	
	public List<TradingPair> getTradingPairs(SqlSession sqlSession) {
		
		if(this.tradingPairs == null) {
			List<TradingPair> ret = new ArrayList<TradingPair>();
			
			CurrencyPairMapper mapper = sqlSession.getMapper(CurrencyPairMapper.class);
			CurrencyPairExample example = new CurrencyPairExample();
			example.setOrderByClause("tpp_order ASC");
			
			List<CurrencyPair> pairs = mapper.selectByExample(example);
			
			TPCurrencyMapper currencyMapper = sqlSession.getMapper(TPCurrencyMapper.class);
			
			for(CurrencyPair pair : pairs) {
				TradingPair tradingPair = new TradingPair();
				
				tradingPair.setPairId(pair.getTpp_pair_id());
				tradingPair.setFirstCurrencyId(pair.getTpp_first_currency());
				tradingPair.setSecondCurrencyId(pair.getTpp_second_currency());
				
				TPCurrency cur1 = currencyMapper.selectByPrimaryKey(new Integer(pair.getTpp_first_currency()));
				TPCurrency cur2 = currencyMapper.selectByPrimaryKey(new Integer(pair.getTpp_second_currency()));
				
				String firstCode = cur1.getCur_code();
				String secondCode = cur2.getCur_code();
				
				tradingPair.setFirstCurrencyCode(firstCode);
				tradingPair.setSecondCurrencyCode(secondCode);
				
				ret.add(tradingPair);
			}
			
			this.tradingPairs = ret;
		}
		
		return this.tradingPairs;
		
	}
	
}
